home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 May: Tool Chest / Developer CD Series Tool Chest (Apple Computer)(May 1999).iso / Tool Chest / Development Kits / MPW etc / MPW-GM / MPW / Examples / PPCExamples / International Example / InlineInputSample / InlineInputSample.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-12-03  |  66.1 KB  |  2,308 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        InlineInputSample.c
  3.  
  4.     Contains:    C source file for InlineInputSample
  5.  
  6.     Copyright:    © 1989-1994 Apple Computer, Inc. All rights reserved.
  7.  
  8. */
  9.  
  10.  
  11. /* Segmentation strategy:
  12.  
  13.    There isn't any. Depending on which compiler you use, and whether you build for
  14.    Mac-OS 68K, A/UX, or PowerPC, segments may look different or not exist at all.
  15.    And with an application this small, segments don't really matter any more these
  16.    days. So we don't try to figure out segmentation and segment unloading strategies.
  17.    If you want to find out how to segment your application, look at some other
  18.    sample code.
  19. */
  20.  
  21.  
  22. /* SetPort strategy:
  23.  
  24.    Toolbox routines do not change the current port. In spite of this, in this
  25.    program we use a strategy of calling SetPort whenever we want to draw or
  26.    make calls which depend on the current port. This makes us less vulnerable
  27.    to bugs in other software which might alter the current port (such as the
  28.    bug (feature?) in many desk accessories which change the port on OpenDeskAcc).
  29.    Hopefully, this also makes the routines from this program more self-contained,
  30.    since they don't depend on the current port setting.
  31. */
  32.  
  33.  
  34. /* Clipboard strategy:
  35.  
  36.    Under styled TextEdit, TECut and TECopy will write both the text and associated
  37.    style information directly to the desk scrap as types 'TEXT' and 'styl'.
  38.    Instead of using TEToScrap and TEFromScrap, a new routine TEStylPaste, will 
  39.    transfer the text and style from the desk scrap to the document.
  40. */
  41.  
  42.  
  43. #if qInline
  44. #define qAppleEvents 1
  45. #endif
  46.  
  47.  
  48. #include <Limits.h>
  49. #include <Types.h>
  50. #include <QuickDraw.h>
  51. #include <Fonts.h>
  52. #include <Controls.h>
  53. #include <Windows.h>
  54. #include <TextEdit.h>
  55. #include <Dialogs.h>
  56. #include <Menus.h>
  57. #include <Devices.h>
  58. #include <Events.h> 
  59. #include <Scrap.h>
  60. #include <TextUtils.h>
  61. #include <ToolUtils.h>
  62. #include <MacMemory.h>
  63. #include <Processes.h>
  64. #include <Files.h>
  65. #include <OSUtils.h>
  66. #include <Packages.h>
  67. #include <Traps.h>
  68. #include <Printing.h>
  69. #include <DiskInit.h>
  70. #include <FCntl.h>
  71. #include <MacRuntime.h>
  72. #include <StringCompare.h>
  73.  
  74. #if qAppleEvents
  75. #include <Errors.h>
  76. #include <Gestalt.h>
  77. #include <AppleEvents.h>
  78. #if qInline
  79. #include <TextServices.h>
  80. #include <Script.h>
  81. #include "TSMTE.h"
  82. #endif // qInline
  83. #endif // qAppleEvents
  84.  
  85. #include "InlineInputSample.h"
  86.  
  87.  
  88. // Constants
  89.  
  90. // top left corner of the disk initialization dialog
  91.  
  92. const short kDITop = 80;
  93. const short kDILeft = 112;
  94.  
  95. // the number of pixels we leave blank at the edge of the window
  96.  
  97. const short kTextMargin = 2;
  98.  
  99. // the maximum number of open documents at any one time. SetupMenus respects this
  100. // number, but the Apple event handlers don't.
  101.  
  102. const short kMaxOpenDocuments = 4;
  103.     
  104. // arbitrary number used to specify the width of the TERec's destination
  105. // rectangle so that word wrap and horizontal scrolling can be demonstrated
  106.  
  107. const short kMaxDocWidth = 576;
  108.     
  109. // the minimum dimension of a window for GrowWindow
  110.  
  111. const short kMinDocDim = 64;
  112.  
  113. // control contrlVis values to prevent or enable redrawing controls by
  114. // Control Manager routines such as SetCtlValue
  115.  
  116. const unsigned char kControlInvisible = 0;
  117. const unsigned char kControlVisible = 0xff;
  118.  
  119. // for calculating scroll bar positions and sizes
  120.  
  121. const short kScrollbarWidth = 16;
  122. const short kScrollbarAdjust = 15; // should be kScrollbarWidth - 1, but C is too stupid
  123. const short kScrollTweek = 2;
  124.     
  125. // ASCII code for delete character
  126.  
  127. const unsigned char kDelChar = 8;
  128.     
  129. // pixels to scroll when the button part of the horizontal scrollbar is pressed
  130.  
  131. const short kButtonScroll = 4;
  132.  
  133. // maximum text length we allow in a TERec; lower than 32767 to prevent errors
  134.  
  135. const short kMaxTELength = 32000;
  136.  
  137. // the SysEnvRec version we understand
  138.  
  139. const short kSysEnvironsVersion = 1;
  140.  
  141. // events mask for no events
  142.  
  143. const short kNoEventsMask = 0;
  144.  
  145. // the minimum heap size and minimal available heap space we require for running.
  146. // These are rough guesses; with styled TextEdit and printing you never really know.
  147.  
  148. const Size kMinHeap = 50 * 1024;
  149. const Size kMinSpace = 40 * 1024;
  150.  
  151. // values for setting up wide open rectangles and regions
  152.  
  153. const short kExtremeNeg = -32768;
  154. const short kExtremePos = 32767 -1; // required to address an old region bug
  155.  
  156. // extra security when pre-flighting edit commands
  157.  
  158. const short kTESlop = 1024;
  159.  
  160.  
  161. // Types
  162.  
  163. // A DocumentRecord contains the WindowRecord for one of our document windows,
  164. // as well as the TEHandle for the text we are editing. Other document fields
  165. // can be added to this record as needed. This is similar to how the
  166. // Window Manager and Dialog Manager add fields after the GrafPort.
  167.  
  168. typedef struct {
  169.     WindowRecord    docWindow;
  170.     TEHandle        docTE;
  171.     ControlHandle    docVScroll;
  172.     ControlHandle    docHScroll;
  173.     TEClickLoopUPP    docClick;
  174.     Boolean            modified;
  175. #if qInline
  176.     TSMTERecHandle    docTSMTERecHandle;
  177.     TSMDocumentID    docTSMDoc;
  178. #endif
  179. } DocumentRecord, *DocumentPeek;
  180.  
  181.  
  182. // Global Variables
  183.  
  184. // environment information that's set up during initialization
  185.  
  186. Boolean gHasWaitNextEvent;        // WaitNextEvent trap is available
  187. #if qAppleEvents
  188. Boolean gHasAppleEvents;        // Apple events are available, so we expect to get events from the Finder
  189. #if qInline
  190. Boolean gHasTextServices;        // Text Services Manager is available and should be used
  191. Boolean gHasTSMTE;                // Text Services for Text Edit are available and should be used
  192.                                 // gHasTSMTE can only be set if gHasTextServices.
  193. #endif // qInline
  194. #endif // qAppleEvents
  195.  
  196. // whether we are currently in the background. This accounts for major switches only.
  197.  
  198. Boolean gInBackground;
  199.  
  200. // the number of documents currently open
  201.  
  202. short gNumDocuments;
  203.  
  204. // print record shared among all documents ??? probably should be attached to documents instead
  205.  
  206. THPrint gPrinterRecord;
  207.  
  208. // universal procedure pointers
  209.  
  210. ControlActionUPP gHActionUPP;
  211. ControlActionUPP gVActionUPP;
  212. #if powerc
  213. TEClickLoopUPP gClickLoopUPP;
  214. #endif
  215. #if qAppleEvents
  216. AEEventHandlerUPP gHandleOAppUPP;
  217. AEEventHandlerUPP gHandleDocUPP;
  218. AEEventHandlerUPP gHandleQuitUPP;
  219. #if qInline
  220. TSMTEPreUpdateUPP gTSMTEPreUpdateUPP;
  221. TSMTEPostUpdateUPP gTSMTEPostUpdateUPP;
  222. #endif // qInline
  223. #endif // qAppleEvents
  224.  
  225. QDGlobals qd;
  226.  
  227. #if qAppleEvents
  228. // to indicate that Quit command or Apple event was successful
  229.  
  230. Boolean gQuitting;
  231.  
  232. #if qInline
  233. // variable to keep outside fontForce information
  234.  
  235. long gSavedFontForce;
  236.  
  237. #endif // qInline
  238. #endif // qAppleEvents
  239.  
  240.  
  241. // Routine Declarations
  242.  
  243. void AlertUser(short error);
  244. void EventLoop(void);
  245. void DoEvent(EventRecord *event);
  246. void AdjustCursor(Point mouse, RgnHandle region);
  247. void GetGlobalMouse(Point *mouse);
  248. void DoGrowWindow(WindowPtr window, EventRecord *event);
  249. void DoZoomWindow(WindowPtr window, short part);
  250. void ResizedWindow(WindowPtr window);
  251. void GetLocalUpdateRgn(WindowPtr window, RgnHandle localRgn);
  252. void DoUpdate(WindowPtr window);
  253. void DoActivate(WindowPtr window, Boolean becomingActive);
  254. void DoContentClick(WindowPtr window, EventRecord *event);
  255. void DoKeyDown(EventRecord *event);
  256. unsigned long GetSleep(void);
  257. void CommonAction(ControlHandle control, short *amount);
  258. pascal void VActionProc(ControlHandle control, short part);
  259. pascal void HActionProc(ControlHandle control, short part);
  260. void DoIdle(void);
  261. void DrawWindow(WindowPtr window);
  262. void AdjustMenus(void);
  263. void DoMenuCommand(long menuResult);
  264. void DoNew(void);
  265. Boolean DoCloseWindow(WindowPtr window);
  266. #if qAppleEvents
  267. static void PrepareToQuit(void);
  268. #else // qAppleEvents
  269. static void Terminate(void);
  270. #endif // qAppleEvents
  271. void Initialize(void);
  272. void BigBadError(short error);
  273. static void FailNilUPP(UniversalProcPtr theUPP);
  274. void GetTERect(WindowPtr window, Rect *teRect);
  275. void AdjustViewRect(TEHandle docTE);
  276. void AdjustTE(WindowPtr window);
  277. void AdjustHV(Boolean isVert, ControlHandle control, TEHandle docTE, Boolean canRedraw);
  278. void AdjustScrollValues(WindowPtr window, Boolean canRedraw);
  279. void AdjustScrollSizes(WindowPtr window);
  280. void AdjustScrollbars(WindowPtr window, Boolean needsResize);
  281. #if powerc
  282. pascal Boolean ClickLoopProc(TEPtr pTE);
  283. #else
  284. extern pascal void AsmClickLoopProc(void);
  285. #endif
  286. pascal void ClickLoopAddOn(void);
  287. pascal TEClickLoopUPP GetOldClickLoop(void);
  288. Boolean IsDocumentWindow(WindowPtr window);
  289. Boolean IsDAWindow(WindowPtr window);
  290. static void PrintText(TEHandle theText);
  291. #if qAppleEvents
  292. static void CheckAppleEvents(void);
  293. static OSErr InstallRequiredAppleEvents(void);
  294. static OSErr GotRequiredParameters(const AppleEvent *theAppleEvent);
  295. pascal OSErr HandleOAppEvent(const AppleEvent *theEvent, const AppleEvent *reply, long refCon);
  296. pascal OSErr HandleDocEvent(const AppleEvent *theEvent, const AppleEvent *reply, long refCon);
  297. pascal OSErr HandleQuitEvent(const AppleEvent *theEvent, const AppleEvent *reply, long refCon);
  298. #if qInline
  299. static void CheckForTextServices(void);
  300. static pascal void MyTSMTEPreUpdateProc(TEHandle textH, long refCon);
  301. static pascal void MyTSMTEPostUpdateProc(TEHandle textH, long fixLen, long inputAreaStart,
  302.             long inputAreaEnd, long pinStart, long pinEnd, long refCon);
  303. static void ExitApplication(void);
  304. #endif // qInline
  305. #endif // qAppleEvents
  306.  
  307.  
  308.  
  309. // Set up the whole world, including global variables, Toolbox managers.
  310. // If a problem occurs here, we alert the user and exit from the application.
  311.  
  312. void Initialize(void)
  313. {
  314.     EventRecord event;
  315.     short count;
  316.     SysEnvRec systemEnvironment;
  317.     long total, contig;
  318.     Handle menuBar;
  319.  
  320.     gInBackground = false;
  321. #if qAppleEvents
  322.     gQuitting = false;
  323. #endif // qAppleEvents
  324.  
  325.     InitGraf((Ptr) &qd.thePort);
  326.     InitFonts();
  327.     InitWindows();
  328.     InitMenus();
  329.     TEInit();
  330.     InitDialogs(nil);
  331.     InitCursor();
  332.  
  333.     // the following loop is necessary to allow the default button of our
  334.     // alert to be outlined. We use EventAvail instead of GetNextEvent so we
  335.     // don't lose events.
  336.     
  337.     for (count = 1; count <= 3; count++)
  338.         EventAvail(everyEvent, &event);
  339.     
  340.     // collect environment information
  341.  
  342.     // ignore the error returned from SysEnvirons; even if an error occurred,
  343.     // the SysEnvirons glue will fill in the SysEnvRec.
  344.      
  345.     SysEnvirons(kSysEnvironsVersion, &systemEnvironment);
  346.     
  347.     // make sure that the machine has at least 128K ROMs. If it doesn't, exit.
  348.     
  349.     if (systemEnvironment.machineType < 0)
  350.         BigBadError(eOldROM);
  351.     
  352.     // also, require at least system 6.0. The app should be able to run on system
  353.     // software 4.1 (where styled TextEdit was introduced) and later, but it's not really
  354.     // worth the trouble of testing on all those old systems anymore.
  355.     
  356.     if (systemEnvironment.systemVersion < 0x600)
  357.         BigBadError(eOldSystemSoftware);
  358.     
  359.     // It is better to first check the size of the application heap against a value
  360.     // that you have determined is the smallest heap the application can reasonably
  361.     // work in. This number should be derived by examining the size of the heap that
  362.     // is actually provided by MultiFinder when the minimum size requested is used.
  363.     // The check should be made because the preferred size can end up being set smaller
  364.     // than the minimum size by the user. This extra check acts to insure that your
  365.     // application is starting from a solid memory foundation.
  366.      
  367.     if ((long) GetApplLimit() - (long) ApplicationZone() < kMinHeap)
  368.         BigBadError(eSmallSize);
  369.     
  370.     // Next, make sure that enough memory is free for your application to run. It
  371.     // is possible for a situation to arise where the heap may have been of required
  372.     // size, but a large scrap was loaded which left too little memory. To check for
  373.     // this, call PurgeSpace and compare the result with a value that you have determined
  374.     // is the minimum amount of free memory your application needs at initialization.
  375.     // This number can be derived several different ways. One way that is fairly
  376.     // straightforward is to run the application in the minimum size configuration
  377.     // as described previously. Call PurgeSpace at initialization and examine the value
  378.     // returned. However, you should make sure that this result is not being modified
  379.     // by the scrap's presence. You can do that by calling ZeroScrap before calling
  380.     // PurgeSpace. Make sure to remove that call before shipping, though.
  381.     
  382.     // ZeroScrap();
  383.  
  384.     PurgeSpace(&total, &contig);
  385.     if (total < kMinSpace)
  386.         if (UnloadScrap() != noErr)
  387.             BigBadError(eNoMemory);
  388.         else
  389.         {
  390.             PurgeSpace(&total, &contig);
  391.             if (total < kMinSpace)
  392.                 BigBadError(eNoMemory);
  393.         };
  394.  
  395.     // The extra benefit to waiting until after the Toolbox Managers have been initialized
  396.     // to check memory is that we can now give the user an alert to tell him/her what
  397.     // happened. Although it is possible that the memory situation could be worsened by
  398.     // displaying an alert, MultiFinder would gracefully exit the application with
  399.     // an informative alert if memory became critical. Here we are acting more
  400.     // in a preventative manner to avoid future disaster from low-memory problems.
  401.  
  402.     // check for newer system services and set up our environment to make use of what's available.
  403.     
  404.     gHasWaitNextEvent = TrapAvailable(_WaitNextEvent);
  405.  
  406. #if qAppleEvents
  407.     CheckAppleEvents();
  408.     if (gHasAppleEvents)
  409.         (void) InstallRequiredAppleEvents();
  410. #if qInline
  411.  
  412.     CheckForTextServices();
  413.     
  414.     // this application uses TextEdit as the only text engine, and we don't support
  415.     // inline input without TSMTE. Therefore we call InitTSMAwareApplication only if
  416.     // TSMTE is available. A word processor that uses TextEdit only for dialogs
  417.     // and uses Text Services directly with the word processing engine would make this
  418.     // call depend on gHasTextServices.
  419.     
  420.     if (!(gHasTSMTE && InitTSMAwareApplication() == noErr))
  421.     {
  422.         // if this happens, just move on without text services
  423.         gHasTextServices = false;
  424.         gHasTSMTE = false;
  425.     };
  426.     
  427.     // get global fontForce flag, make sure it's off whenever we run
  428.     
  429.     gSavedFontForce = GetScriptManagerVariable(smFontForce);
  430.     (void) SetScriptManagerVariable(smFontForce, 0);
  431. #endif // qInline
  432. #endif // qAppleEvents
  433.  
  434.     // set up the menu bar and the menus that depend on the system environment
  435.     
  436.     menuBar = GetNewMBar(rMenuBar);    
  437.     if ( menuBar == nil )
  438.         BigBadError(eNoMemory);
  439.     SetMenuBar(menuBar);
  440.     DisposeHandle(menuBar);
  441.     AppendResMenu(GetMenuHandle(mApple), 'DRVR');    // build the Apple menu
  442.     AppendResMenu(GetMenuHandle(mFont), 'FONT');    // build the Font menu
  443.     DrawMenuBar();
  444.  
  445.     // we have no document open yet
  446.     
  447.     gNumDocuments = 0;
  448.  
  449.     // set up printer stuff - this will allow the default page setup parameters to be used,
  450.     // so if the user decides to print without using the Page Setup command everything will
  451.     // be OK
  452.     
  453.     gPrinterRecord = (THPrint) NewHandle(sizeof(TPrint));
  454.     if (gPrinterRecord != nil)
  455.     {
  456.         // if we got a print handle, initialize it to default values
  457.         PrOpen();
  458.         PrintDefault(gPrinterRecord);
  459.         PrClose();
  460.     };
  461.     
  462.     // initialize the universal procedure pointers that we need
  463. #if qAppleEvents
  464.     // (Apple event handler UPPs are set up in InstallRequiredAppleEvents)
  465. #endif // qAppleEvents
  466.     
  467.     gHActionUPP = NewControlActionProc(HActionProc);
  468.     FailNilUPP((UniversalProcPtr) gHActionUPP);
  469.     gVActionUPP = NewControlActionProc(VActionProc);
  470.     FailNilUPP((UniversalProcPtr) gVActionUPP);
  471. #if powerc
  472.     gClickLoopUPP = NewTEClickLoopProc(ClickLoopProc);
  473.     FailNilUPP((UniversalProcPtr) gClickLoopUPP);
  474. #endif
  475. #if qInline
  476.     if (gHasTSMTE)
  477.     {
  478.         gTSMTEPreUpdateUPP = NewTSMTEPreUpdateProc(MyTSMTEPreUpdateProc);
  479.         FailNilUPP((UniversalProcPtr) gTSMTEPreUpdateUPP);
  480.         gTSMTEPostUpdateUPP = NewTSMTEPostUpdateProc(MyTSMTEPostUpdateProc);
  481.         FailNilUPP((UniversalProcPtr) gTSMTEPostUpdateUPP);
  482.     };
  483. #endif // qInline
  484. }
  485.  
  486.  
  487. // report a fatal error to the user and exit from the application
  488.  
  489. void BigBadError(short error)
  490. {
  491.     AlertUser(error);
  492. #if qInline
  493.     ExitApplication();
  494. #else // qInline
  495.     ExitToShell();
  496. #endif // qInline
  497. }
  498.  
  499.  
  500. // check whether a valid UPP was allocated
  501.  
  502. static void FailNilUPP(UniversalProcPtr theUPP)
  503. {
  504.     if (theUPP == nil)
  505.         BigBadError(eNoMemory);
  506. }
  507.  
  508. #if qAppleEvents
  509.  
  510. // check to see if a given bit in a long word is set.
  511.  
  512. static Boolean BTst(long value, short bit)
  513. {
  514.     long mask = 1L << bit;
  515.     
  516.     return (value & mask) == mask;
  517. }
  518.  
  519. #endif // qAppleEvents
  520.  
  521.  
  522. #if qAppleEvents
  523.  
  524. static void CheckAppleEvents(void)
  525. {
  526.     long gestaltResponse;
  527.     
  528.     gHasAppleEvents = false;
  529.     
  530.     if (TrapAvailable(_Gestalt))
  531.     {
  532.         if (Gestalt(gestaltAppleEventsAttr, &gestaltResponse) == noErr)
  533.             gHasAppleEvents = BTst(gestaltResponse, gestaltAppleEventsPresent);
  534.     };
  535. }
  536.  
  537. #if qInline
  538.  
  539. // check whether the Text Services Manager and the extension for using Text Services with
  540. // TextEdit (TSMTE) are available, and sets gHasTextServices and gHasTSMTE accordingly.
  541.  
  542. static void CheckForTextServices(void)
  543. {
  544.     long gestaltResponse;
  545.     
  546.     gHasTextServices = false;        // unless proven otherwise
  547.     gHasTSMTE = false;                // unless proven otherwise
  548.     
  549.     if (TrapAvailable(_Gestalt))
  550.     {
  551.         if ((Gestalt(gestaltTSMgrVersion, &gestaltResponse) == noErr) && (gestaltResponse >= 1))
  552.         {
  553.             gHasTextServices = true;
  554.             if (Gestalt(gestaltTSMTEAttr, &gestaltResponse) == noErr)
  555.                 gHasTSMTE = BTst(gestaltResponse, gestaltTSMTEPresent);
  556.         };
  557.     };
  558. }
  559.  
  560. #endif // qInline
  561. #endif // qAppleEvents
  562.  
  563. void main(void)
  564. {
  565.     /*    If you have stack requirements that differ from the default,
  566.         then you could use SetApplLimit to increase StackSpace at 
  567.         this point, before calling MaxApplZone. */
  568.     MaxApplZone();                    /* expand the heap */
  569.  
  570.     Initialize();                    /* initialize the program */
  571. #if qAppleEvents
  572.     if (!gHasAppleEvents)
  573.         DoNew();
  574. #else // qAppleEvents
  575.     DoNew();
  576. #endif // qAppleEvents
  577.  
  578.     EventLoop();                    /* call the main event loop */
  579.  
  580. #if qAppleEvents
  581. #if qInline
  582.     ExitApplication();
  583. #else // qInline
  584.     ExitToShell();
  585. #endif // qInline
  586. #endif // qAppleEvents
  587. }
  588.  
  589. #if qInline
  590.  
  591. static Boolean IntlTSMEvent(EventRecord *event)
  592. {
  593.     short oldFont;
  594.     ScriptCode keyboardScript;
  595.     
  596.     // make sure we have a port and it's not the Window Manager port
  597.     if (qd.thePort != nil && FrontWindow() != nil)
  598.     {
  599.         oldFont = qd.thePort->txFont;
  600.         keyboardScript = GetScriptManagerVariable(smKeyScript);
  601.         if (FontToScript(oldFont) != keyboardScript)
  602.             TextFont(GetScriptVariable(keyboardScript, smScriptAppFond));
  603.     };
  604.     return TSMEvent(event);
  605. }
  606.  
  607. #endif // qInline
  608.  
  609. /* Get events forever, and handle them by calling DoEvent.
  610.    Also call AdjustCursor each time through the loop. */
  611.  
  612. void EventLoop(void)
  613. {
  614.     RgnHandle    cursorRgn;
  615.     Boolean        gotEvent;
  616.     EventRecord    event;
  617.     Point        mouse;
  618.  
  619.     cursorRgn = NewRgn();            /* we’ll pass WNE an empty region the 1st time thru */
  620. #if qAppleEvents
  621.     while (!gQuitting)
  622. #else // qAppleEvents
  623.     while (true) // loop forever, quit via ExitToShell
  624. #endif // qAppleEvents
  625.     {
  626. #if qInline
  627.         // set global fontForce flag so other apps don't get confused
  628.         (void) SetScriptManagerVariable(smFontForce, gSavedFontForce);
  629.         
  630. #endif // qInline
  631.         /* use WNE if it is available */
  632.         if ( gHasWaitNextEvent ) {
  633.             GetGlobalMouse(&mouse);
  634.             AdjustCursor(mouse, cursorRgn);
  635.             gotEvent = WaitNextEvent(everyEvent, &event, GetSleep(), cursorRgn);
  636.         }
  637.         else {
  638.             SystemTask();
  639.             gotEvent = GetNextEvent(everyEvent, &event);
  640.         };
  641.  
  642. #if qInline
  643.         // clear fontForce again so it doesn't upset our operations
  644.         gSavedFontForce = GetScriptManagerVariable(smFontForce);
  645.         (void) SetScriptManagerVariable(smFontForce, 0);
  646.         
  647.         if ( gotEvent && !(gHasTextServices && IntlTSMEvent(&event)))
  648. #else // qInline
  649.         if ( gotEvent )
  650. #endif
  651.         {
  652.             /* make sure we have the right cursor before handling the event */
  653.             AdjustCursor(event.where, cursorRgn);
  654.             DoEvent(&event);
  655.         }
  656.         else
  657.             DoIdle();                /* perform idle tasks when it’s not our event */
  658.         /*    If you are using modeless dialogs that have editText items,
  659.             you will want to call IsDialogEvent to give the caret a chance
  660.             to blink, even if WNE/GNE returned FALSE. However, check FrontWindow
  661.             for a non-NIL value before calling IsDialogEvent. */
  662.     };
  663. }
  664.  
  665.  
  666. /* Do the right thing for an event. Determine what kind of event it is, and call
  667.  the appropriate routines. */
  668.  
  669. void DoEvent(EventRecord *event)
  670. {
  671.     short        part, err;
  672.     WindowPtr    window;
  673.     char        key;
  674.     Point        aPoint;
  675. #if qInline
  676.     long        menuResult;
  677. #endif // qInline
  678.  
  679.     switch ( event->what ) {
  680.         case nullEvent:
  681.             /* we idle for null/mouse moved events ands for events which aren’t
  682.                 ours (see EventLoop) */
  683.             DoIdle();
  684.             break;
  685.         case mouseDown:
  686.             part = FindWindow(event->where, &window);
  687.             switch ( part ) {
  688.                 case inMenuBar:             /* process a mouse menu command (if any) */
  689.                     AdjustMenus();
  690. #if qInline
  691.                     menuResult = MenuSelect(event->where);
  692.                     if (!(gHasTextServices && TSMMenuSelect(menuResult)))
  693.                         DoMenuCommand(menuResult);
  694.                     HiliteMenu(0); // needed even if TSM or Script Manager handle the menu
  695. #else // qInline
  696.                     DoMenuCommand(MenuSelect(event->where));
  697. #endif // qInline
  698.                     break;
  699.                 case inSysWindow:           /* let the system handle the mouseDown */
  700.                     SystemClick(event, window);
  701.                     break;
  702.                 case inContent:
  703.                     if ( window != FrontWindow() ) {
  704.                         SelectWindow(window);
  705.                         AdjustMenus();
  706.                     } else
  707.                         DoContentClick(window, event);
  708.                     break;
  709.                 case inDrag:                /* pass screenBits.bounds to get all gDevices */
  710.                     DragWindow(window, event->where, &qd.screenBits.bounds);
  711.                     break;
  712.                 case inGoAway:
  713.                     if ( TrackGoAway(window, event->where) )
  714.                         DoCloseWindow(window); /* we don’t care if the user cancelled */
  715.                     break;
  716.                 case inGrow:
  717.                     DoGrowWindow(window, event);
  718.                     break;
  719.                 case inZoomIn:
  720.                 case inZoomOut:
  721.                 if ( TrackBox(window, event->where, part) )
  722.                         DoZoomWindow(window, part);
  723.                     break;
  724.             }
  725.             break;
  726.         case keyDown:
  727.         case autoKey:                       /* check for menukey equivalents */
  728.             key = event->message & charCodeMask;
  729.             if ( event->modifiers & cmdKey ) {    /* Command key down */
  730.                 if ( event->what == keyDown ) {
  731.                     AdjustMenus();            /* enable/disable/check menu items properly */
  732.                     DoMenuCommand(MenuKey(key));
  733.                 }
  734.             } else
  735.                 DoKeyDown(event);
  736.             break;
  737.         case activateEvt:
  738.             DoActivate((WindowPtr) event->message, (event->modifiers & activeFlag) != 0);
  739.             break;
  740.         case updateEvt:
  741.             DoUpdate((WindowPtr) event->message);
  742.             break;
  743.         /*    1.01 - It is not a bad idea to at least call DIBadMount in response
  744.             to a diskEvt, so that the user can format a floppy. */
  745.         case diskEvt:
  746.             if ( HiWord(event->message) != noErr ) {
  747.                 SetPt(&aPoint, kDILeft, kDITop);
  748.                 err = DIBadMount(aPoint, event->message);
  749.             }
  750.             break;
  751.         case osEvt:
  752.         /*    1.02 - must BitAND with 0x0FF to get only low byte */
  753.             switch ((event->message >> 24) & 0x0FF) {        /* high byte of message */
  754.                 case mouseMovedMessage:
  755.                     DoIdle();                    /* mouse-moved is also an idle event */
  756.                     break;
  757.                 case suspendResumeMessage:        /* suspend/resume is also an activate/deactivate */
  758.                     gInBackground = (event->message & resumeFlag) == 0;
  759.                     DoActivate(FrontWindow(), !gInBackground);
  760.                     break;
  761.             }
  762.             break;
  763. #if qAppleEvents
  764.         case kHighLevelEvent:
  765.             if (AEProcessAppleEvent(event) != noErr)
  766.                 ; // any ideas for error handling?
  767.             break;
  768. #endif // qAppleEvents
  769.     }
  770. } /*DoEvent*/
  771.  
  772.  
  773. /*    Change the cursor's shape, depending on its position. This also calculates the region
  774.     where the current cursor resides (for WaitNextEvent). When the mouse moves outside of
  775.     this region, an event is generated. If there is more to the event than just
  776.     “the mouse moved”, we get called before the event is processed to make sure
  777.     the cursor is the right one. In any (ahem) event, this is called again before we
  778.     fall back into WNE. */
  779.  
  780. void AdjustCursor(Point mouse, RgnHandle region)
  781. {
  782.     WindowPtr    window;
  783.     RgnHandle    arrowRgn;
  784.     RgnHandle    iBeamRgn;
  785.     Rect        iBeamRect;
  786.  
  787.     window = FrontWindow();    /* we only adjust the cursor when we are in front */
  788.     if ( (! gInBackground) && (! IsDAWindow(window)) ) {
  789.         /* calculate regions for different cursor shapes */
  790.         arrowRgn = NewRgn();
  791.         iBeamRgn = NewRgn();
  792.  
  793.         /* start arrowRgn wide open */
  794.         SetRectRgn(arrowRgn, kExtremeNeg, kExtremeNeg, kExtremePos, kExtremePos);
  795.  
  796.         /* calculate iBeamRgn */
  797.         if ( IsDocumentWindow(window) ) {
  798.             iBeamRect = (*((DocumentPeek) window)->docTE)->viewRect;
  799.             SetPort(window);    /* make a global version of the viewRect */
  800.             // ??? following two lines depend on Rect structure layout
  801.             LocalToGlobal((Point *) &(iBeamRect).top);
  802.             LocalToGlobal((Point *) &(iBeamRect).bottom);
  803.             RectRgn(iBeamRgn, &iBeamRect);
  804.             /* we temporarily change the port’s origin to “globalfy” the visRgn */
  805.             SetOrigin(-window->portBits.bounds.left, -window->portBits.bounds.top);
  806.             SectRgn(iBeamRgn, window->visRgn, iBeamRgn);
  807.             SetOrigin(0, 0);
  808.         }
  809.  
  810.         /* subtract other regions from arrowRgn */
  811.         DiffRgn(arrowRgn, iBeamRgn, arrowRgn);
  812.  
  813. #if qInline
  814.         // before we commit to anything, let's check whether some text service has a
  815.         // different idea
  816.         
  817.         if (!(gHasTextServices && SetTSMCursor(mouse)))
  818.         {
  819.             // change the cursor and the region parameter
  820.             if (PtInRgn(mouse, iBeamRgn))
  821.             {
  822.                 SetCursor(*GetCursor(iBeamCursor));
  823.                 CopyRgn(iBeamRgn, region);
  824.             }
  825.             else
  826.             {
  827.                 SetCursor(&qd.arrow);
  828.                 CopyRgn(arrowRgn, region);
  829.             };
  830.         };
  831.  
  832.         // and no matter how nice the region, with text services it cannot be bigger than
  833.         // a point. Yes, this defeats the purpose of all the calculations...
  834.         
  835.         if (gHasTextServices)
  836.             SetRectRgn(region, mouse.h, mouse.v, mouse.h, mouse.v);
  837. #else // qInline
  838.         // change the cursor and the region parameter
  839.         if (PtInRgn(mouse, iBeamRgn))
  840.         {
  841.             SetCursor(*GetCursor(iBeamCursor));
  842.             CopyRgn(iBeamRgn, region);
  843.         }
  844.         else
  845.         {
  846.             SetCursor(&qd.arrow);
  847.             CopyRgn(arrowRgn, region);
  848.         };
  849. #endif // qInline
  850.  
  851.         DisposeRgn(arrowRgn);
  852.         DisposeRgn(iBeamRgn);
  853.     }
  854. } /*AdjustCursor*/
  855.  
  856.  
  857. /*    Get the global coordinates of the mouse. When you call OSEventAvail
  858.     it will return either a pending event or a null event. In either case,
  859.     the where field of the event record will contain the current position
  860.     of the mouse in global coordinates and the modifiers field will reflect
  861.     the current state of the modifiers. Another way to get the global
  862.     coordinates is to call GetMouse and LocalToGlobal, but that requires
  863.     being sure that thePort is set to a valid port. */
  864.  
  865. void GetGlobalMouse(Point *mouse)
  866. {
  867.     EventRecord    event;
  868.     
  869.     OSEventAvail(kNoEventsMask, &event);    /* we aren't interested in any events */
  870.     *mouse = event.where;                /* just the mouse position */
  871. } /*GetGlobalMouse*/
  872.  
  873.  
  874. /*    Called when a mouseDown occurs in the grow box of an active window. In
  875.     order to eliminate any 'flicker', we want to invalidate only what is
  876.     necessary. Since ResizedWindow invalidates the whole portRect, we save
  877.     the old TE viewRect, intersect it with the new TE viewRect, and
  878.     remove the result from the update region. However, we must make sure
  879.     that any old update region that might have been around gets put back. */
  880.  
  881. void DoGrowWindow(WindowPtr window, EventRecord *event)
  882. {
  883.     long        growResult;
  884.     Rect        tempRect;
  885.     RgnHandle    tempRgn;
  886.     DocumentPeek doc;
  887.     
  888.     tempRect = qd.screenBits.bounds;                    /* set up limiting values */
  889.     tempRect.left = kMinDocDim;
  890.     tempRect.top = kMinDocDim;
  891.     growResult = GrowWindow(window, event->where, &tempRect);
  892.     /* see if it really changed size */
  893.     if ( growResult != 0 ) {
  894.         doc = (DocumentPeek) window;
  895.         tempRect = (*doc->docTE)->viewRect;                /* save old text box */
  896.         tempRgn = NewRgn();
  897.         GetLocalUpdateRgn(window, tempRgn);                /* get localized update region */
  898.         SizeWindow(window, LoWord(growResult), HiWord(growResult), true);
  899.         ResizedWindow(window);
  900.         /* calculate & validate the region that hasn’t changed so it won’t get redrawn */
  901.         SectRect(&tempRect, &(*doc->docTE)->viewRect, &tempRect);
  902.         ValidRect(&tempRect);                            /* take it out of update */
  903.         InvalRgn(tempRgn);                                /* put back any prior update */
  904.         DisposeRgn(tempRgn);
  905.     }
  906. } /* DoGrowWindow */
  907.  
  908.  
  909. /*     Called when a mouseClick occurs in the zoom box of an active window.
  910.     Everything has to get re-drawn here, so we don't mind that
  911.     ResizedWindow invalidates the whole portRect. */
  912.  
  913. void DoZoomWindow(WindowPtr window, short part)
  914. {
  915.     EraseRect(&window->portRect);
  916.     ZoomWindow(window, part, window == FrontWindow());
  917.     ResizedWindow(window);
  918. } /*  DoZoomWindow */
  919.  
  920.  
  921. /* Called when the window has been resized to fix up the controls and content. */
  922. void ResizedWindow(WindowPtr window)
  923. {
  924.     AdjustScrollbars(window, true);
  925.     AdjustTE(window);
  926.     InvalRect(&window->portRect);
  927. } /* ResizedWindow */
  928.  
  929.  
  930. /* Returns the update region in local coordinates */
  931. void GetLocalUpdateRgn(WindowPtr window, RgnHandle localRgn)
  932. {
  933.     CopyRgn(((WindowPeek) window)->updateRgn, localRgn);    /* save old update region */
  934.     OffsetRgn(localRgn, window->portBits.bounds.left, window->portBits.bounds.top);
  935. } /* GetLocalUpdateRgn */
  936.  
  937.  
  938. /*    This is called when an update event is received for a window.
  939.     It calls DrawWindow to draw the contents of an application window.
  940.     As an efficiency measure that does not have to be followed, it
  941.     calls the drawing routine only if the visRgn is non-empty. This
  942.     will handle situations where calculations for drawing or drawing
  943.     itself is very time-consuming. */
  944.  
  945. void DoUpdate(WindowPtr window)
  946. {
  947.     if ( IsDocumentWindow(window) ) {
  948.         BeginUpdate(window);                /* this sets up the visRgn */
  949.         if ( ! EmptyRgn(window->visRgn) )    /* draw if updating needs to be done */
  950.             DrawWindow(window);
  951.         EndUpdate(window);
  952.     }
  953. } /*DoUpdate*/
  954.  
  955.  
  956. /*    This is called when a window is activated or deactivated.
  957.     It calls TextEdit to deal with the selection. */
  958.  
  959. void DoActivate(WindowPtr window, Boolean becomingActive)
  960. {
  961.     RgnHandle    tempRgn, clipRgn;
  962.     Rect        growRect;
  963.     DocumentPeek doc;
  964.     
  965.     if ( IsDocumentWindow(window) ) {
  966.         doc = (DocumentPeek) window;
  967.         if ( becomingActive ) {
  968.             /*    since we don’t want TEActivate to draw a selection in an area where
  969.                 we’re going to erase and redraw, we’ll clip out the update region
  970.                 before calling it. */
  971.             tempRgn = NewRgn();
  972.             clipRgn = NewRgn();
  973.             GetLocalUpdateRgn(window, tempRgn);            /* get localized update region */
  974.             GetClip(clipRgn);
  975.             DiffRgn(clipRgn, tempRgn, tempRgn);            /* subtract updateRgn from clipRgn */
  976.             SetClip(tempRgn);
  977.             TEActivate(doc->docTE);
  978.             SetClip(clipRgn);                            /* restore the full-blown clipRgn */
  979.             DisposeRgn(tempRgn);
  980.             DisposeRgn(clipRgn);
  981.             
  982.             /* the controls must be redrawn on activation: */
  983.             (*doc->docVScroll)->contrlVis = kControlVisible;
  984.             (*doc->docHScroll)->contrlVis = kControlVisible;
  985.             InvalRect(&(*doc->docVScroll)->contrlRect);
  986.             InvalRect(&(*doc->docHScroll)->contrlRect);
  987.             /* the growbox needs to be redrawn on activation: */
  988.             growRect = window->portRect;
  989.             /* adjust for the scrollbars */
  990.             growRect.top = growRect.bottom - kScrollbarAdjust;
  991.             growRect.left = growRect.right - kScrollbarAdjust;
  992.             InvalRect(&growRect);
  993. #if qInline
  994.             if (doc->docTSMDoc != nil)
  995.                 (void) ActivateTSMDocument(doc->docTSMDoc);
  996. #endif // qInline
  997.         }
  998.         else
  999.         {        
  1000. #if qInline
  1001.             if (doc->docTSMDoc != nil)
  1002.                 (void) DeactivateTSMDocument(doc->docTSMDoc);
  1003. #endif // qInline
  1004.             TEDeactivate(doc->docTE);
  1005.             /* the controls must be hidden on deactivation: */
  1006.             HideControl(doc->docVScroll);
  1007.             HideControl(doc->docHScroll);
  1008.             /* the growbox should be changed immediately on deactivation: */
  1009.             DrawGrowIcon(window);
  1010.         }
  1011.     }
  1012. } /*DoActivate*/
  1013.  
  1014.  
  1015. /*    This is called when a mouseDown occurs in the content of a window. */
  1016.  
  1017. void DoContentClick(WindowPtr window, EventRecord *event)
  1018. {
  1019.     Point        mouse;
  1020.     ControlHandle control;
  1021.     short        part, value;
  1022.     Boolean        shiftDown;
  1023.     DocumentPeek doc;
  1024.     Rect        teRect;
  1025.  
  1026.     if ( IsDocumentWindow(window) ) {
  1027.         SetPort(window);
  1028.         mouse = event->where;                            /* get the click position */
  1029.         GlobalToLocal(&mouse);
  1030.         doc = (DocumentPeek) window;
  1031.         /* see if we are in the viewRect. if so, we won’t check the controls */
  1032.         GetTERect(window, &teRect);
  1033.         if ( PtInRect(mouse, &teRect) ) {
  1034.             /* see if we need to extend the selection */
  1035.             shiftDown = (event->modifiers & shiftKey) != 0;    /* extend if Shift is down */
  1036.             TEClick(mouse, shiftDown, doc->docTE);
  1037.         } else {
  1038.             part = FindControl(mouse, window, &control);
  1039.             switch ( part ) {
  1040.                 case 0:                            /* do nothing for viewRect case */
  1041.                     break;
  1042.                 case kControlIndicatorPart:
  1043.                     value = GetControlValue(control);
  1044.                     part = TrackControl(control, mouse, nil);
  1045.                     if ( part != 0 ) {
  1046.                         value -= GetControlValue(control);
  1047.                         /* value now has CHANGE in value; if value changed, scroll */
  1048.                         if ( value != 0 )
  1049.                             if ( control == doc->docVScroll )
  1050.                                 TEScroll(0, value, doc->docTE);
  1051.                             else
  1052.                                 TEScroll(value, 0, doc->docTE);
  1053.                     }
  1054.                     break;
  1055.                 default:                        /* they clicked in an arrow, so track & scroll */
  1056.                     if ( control == doc->docVScroll )
  1057.                         value = TrackControl(control, mouse, gVActionUPP);
  1058.                     else
  1059.                         value = TrackControl(control, mouse, gHActionUPP);
  1060.                     break;
  1061.             }
  1062.         }
  1063.     }
  1064. } /*DoContentClick*/
  1065.  
  1066.  
  1067. /* This is called for any keyDown or autoKey events, except when the
  1068.  Command key is held down. It looks at the frontmost window to decide what
  1069.  to do with the key typed. */
  1070.  
  1071. void DoKeyDown(EventRecord *event)
  1072. {
  1073.     enum { minArrowKey = 28, maxArrowKey = 31 };
  1074.  
  1075.     WindowPtr    window;
  1076.     char        key;
  1077.     TEHandle    te;
  1078.  
  1079.     window = FrontWindow();
  1080.     if ( IsDocumentWindow(window) ) {
  1081.         te = ((DocumentPeek) window)->docTE;
  1082.         key = event->message & charCodeMask;
  1083.         /* we have a char. for our window; see if we are still below TextEdit’s
  1084.             limit for the number of characters (but deletes are always rad) */
  1085.         if ( key == kDelChar ||
  1086.                 (*te)->teLength - ((*te)->selEnd - (*te)->selStart) + 1 <
  1087.                 kMaxTELength )
  1088.         {
  1089.             TEKey(key, te);
  1090.             if ((key < minArrowKey) || (key > maxArrowKey)) // not a cursor key
  1091.                 ((DocumentPeek) window)->modified = true;
  1092.             AdjustScrollbars(window, false);
  1093.         }
  1094.         else
  1095.             AlertUser(eExceedChar);
  1096.     }
  1097. } /*DoKeyDown*/
  1098.  
  1099.  
  1100. /*    Calculate a sleep value for WaitNextEvent. This takes into account the things
  1101.     that DoIdle does with idle time. */
  1102.  
  1103. unsigned long GetSleep(void)
  1104. {
  1105.     long        sleep;
  1106.     WindowPtr    window;
  1107.     TEHandle    te;
  1108.  
  1109.     sleep = LONG_MAX;                        /* default value for sleep */
  1110.     if ( !gInBackground ) {
  1111.         window = FrontWindow();            /* and the front window is ours... */
  1112.         if ( IsDocumentWindow(window) ) {
  1113.             te = ((DocumentPeek) (window))->docTE;    /* and the selection is an insertion point... */
  1114.             if ( (*te)->selStart == (*te)->selEnd )
  1115.                 sleep = GetCaretTime();        /* blink time for the insertion point */
  1116.         }
  1117.     }
  1118.     return sleep;
  1119. } /*GetSleep*/
  1120.  
  1121.  
  1122. /*    Common algorithm for pinning the value of a control. It returns the actual amount
  1123.     the value of the control changed. Note the pinning is done for the sake of returning
  1124.     the amount the control value changed. */
  1125.  
  1126. void CommonAction(ControlHandle control, short *amount)
  1127. {
  1128.     short        value, max;
  1129.     
  1130.     value = GetControlValue(control);    /* get current value */
  1131.     max = GetControlMaximum(control);        /* and maximum value */
  1132.     *amount = value - *amount;
  1133.     if ( *amount < 0 )
  1134.         *amount = 0;
  1135.     else if ( *amount > max )
  1136.         *amount = max;
  1137.     SetControlValue(control, *amount);
  1138.     *amount = value - *amount;        /* calculate the real change */
  1139. } /* CommonAction */
  1140.  
  1141.  
  1142. /* Determines how much to change the value of the vertical scrollbar by and how
  1143.     much to scroll the TE record. */
  1144.  
  1145. pascal void VActionProc(ControlHandle control, short part)
  1146. {
  1147.     short        amount;
  1148.     WindowPtr    window;
  1149.     TEPtr        te;
  1150.     
  1151.     if ( part != 0 ) {                /* if it was actually in the control */
  1152.         window = (*control)->contrlOwner;
  1153.         te = *((DocumentPeek) window)->docTE;
  1154.         switch ( part ) {
  1155.             case kControlUpButtonPart:
  1156.             case kControlDownButtonPart:
  1157.                 amount = 24;
  1158.                 break;
  1159.             case kControlPageUpPart:            /* one page */
  1160.             case kControlPageDownPart:
  1161.                 amount = te->viewRect.bottom - te->viewRect.top;
  1162.                 break;
  1163.         }
  1164.         if ( (part == kControlDownButtonPart) || (part == kControlPageDownPart) )
  1165.             amount = -amount;        /* reverse direction for a downer */
  1166.         CommonAction(control, &amount);
  1167.         if ( amount != 0 )
  1168.             TEScroll(0, amount, ((DocumentPeek) window)->docTE);
  1169.     }
  1170. } /* VActionProc */
  1171.  
  1172.  
  1173. /* Determines how much to change the value of the horizontal scrollbar by and how
  1174. much to scroll the TE record. */
  1175.  
  1176. pascal void HActionProc(ControlHandle control, short part)
  1177. {
  1178.     short        amount;
  1179.     WindowPtr    window;
  1180.     TEPtr        te;
  1181.     
  1182.     if ( part != 0 ) {
  1183.         window = (*control)->contrlOwner;
  1184.         te = *((DocumentPeek) window)->docTE;
  1185.         switch ( part ) {
  1186.             case kControlUpButtonPart:
  1187.             case kControlDownButtonPart:        /* a few pixels */
  1188.                 amount = kButtonScroll;
  1189.                 break;
  1190.             case kControlPageUpPart:            /* a page */
  1191.             case kControlPageDownPart:
  1192.                 amount = te->viewRect.right - te->viewRect.left;
  1193.                 break;
  1194.         }
  1195.         if ( (part == kControlDownButtonPart) || (part == kControlPageDownPart) )
  1196.             amount = -amount;        /* reverse direction */
  1197.         CommonAction(control, &amount);
  1198.         if ( amount != 0 )
  1199.             TEScroll(amount, 0, ((DocumentPeek) window)->docTE);
  1200.     }
  1201. } /* VActionProc */
  1202.  
  1203.  
  1204. /* This is called whenever we get a null event et al.
  1205.  It takes care of necessary periodic actions. For this program, it calls TEIdle. */
  1206.  
  1207. void DoIdle(void)
  1208. {
  1209.     WindowPtr    window;
  1210.  
  1211.     window = FrontWindow();
  1212.     if ( IsDocumentWindow(window) )
  1213.         TEIdle(((DocumentPeek) window)->docTE);
  1214. } /*DoIdle*/
  1215.  
  1216.  
  1217. /* Draw the contents of an application window. */
  1218.  
  1219. void DrawWindow(WindowPtr window)
  1220. {
  1221.     SetPort(window);
  1222.     EraseRect(&window->portRect);
  1223.     DrawControls(window);
  1224.     DrawGrowIcon(window);
  1225.     TEUpdate(&window->portRect, ((DocumentPeek) window)->docTE);
  1226. } /*DrawWindow*/
  1227.  
  1228.  
  1229. // menu handling utilities
  1230.  
  1231. static void DisableMenu(MenuHandle menu)
  1232. {
  1233.     DisableItem(menu, 0);
  1234. }
  1235.  
  1236.  
  1237. static void EnableMenu(MenuHandle menu)
  1238. {
  1239.     EnableItem(menu, 0);
  1240. }
  1241.  
  1242. static void RemoveMenuCheckMarks(MenuHandle menu)
  1243. {
  1244.     short item;
  1245.  
  1246.     for (item = 1; item <= CountMItems(menu); item++)
  1247.     {
  1248.         CheckItem(menu, item, false);
  1249.     };
  1250. }
  1251.  
  1252. static void RemoveMenuStyles(MenuHandle menu)
  1253. {
  1254.     short item;
  1255.  
  1256.     for (item = 1; item <= CountMItems(menu); item++)
  1257.     {
  1258.         SetItemStyle(menu, item, normal);
  1259.     };
  1260. }
  1261.  
  1262. static void EnableItemIf(MenuHandle menu, short item, Boolean condition)
  1263. {
  1264.     if (condition)
  1265.         EnableItem(menu, item);
  1266.     else
  1267.         DisableItem(menu, item);
  1268. }
  1269.  
  1270.  
  1271. // enable and disable menus based on the current state.
  1272. // In general, we set up the menu items only before calling MenuSelect or MenuKey,
  1273. // since these are the only times that a menu item can be selected. However, we
  1274. // also have to set up the menus whenever the front window changes because
  1275. // we enable and disable some menu titles depending on the front window.
  1276.  
  1277. void AdjustMenus(void)
  1278. {
  1279.     WindowPtr    window;
  1280.     Boolean        frontIsDAWindow;
  1281.     Boolean        frontIsDocWindow;
  1282.     Boolean        docHasSelection;
  1283.     Boolean        clipboardHasText;
  1284.     Boolean        haveContinuousFont;
  1285.     MenuHandle    menu;
  1286.     long        offset;
  1287.     TEHandle    te;
  1288.     TextStyle    theTextStyle;
  1289.     short        theFont;
  1290.     short        mode;
  1291.     short        item;
  1292.     
  1293.  
  1294.     // gather some state information that we'll need to decide which menu items to enable
  1295.     window = FrontWindow();
  1296.     frontIsDAWindow = (window != nil) && IsDAWindow(window);
  1297.     frontIsDocWindow = (window != nil) && IsDocumentWindow(window);
  1298.     if (frontIsDocWindow)
  1299.     {
  1300.         te = ((DocumentPeek) window)->docTE;
  1301.         docHasSelection = (*te)->selStart < (*te)->selEnd;
  1302.     }
  1303.     else
  1304.         docHasSelection = false;
  1305.     clipboardHasText = GetScrap(nil, 'TEXT', &offset)  > 0;
  1306.         // note that TEGetScrapLength works for the private TextEdit scrap only, which
  1307.         // is not used by styled TextEdit
  1308.     haveContinuousFont = false; // will really be set when setting up font menu
  1309.     
  1310.     // Apple menu is always enabled
  1311.     
  1312.     // File menu
  1313.     menu = GetMenuHandle(mFile);
  1314.     EnableItemIf(menu, iNew, gNumDocuments < kMaxOpenDocuments);
  1315.     EnableItemIf(menu, iClose, window != nil);
  1316.     EnableItemIf(menu, iPageSetup, frontIsDocWindow);
  1317.     EnableItemIf(menu, iPrint, frontIsDocWindow);
  1318.     EnableItem(menu, iQuit);
  1319.  
  1320.     menu = GetMenuHandle(mEdit);
  1321.     EnableItemIf(menu, iUndo, frontIsDAWindow); // can't handle Undo for documents yet
  1322.     EnableItemIf(menu, iCut, frontIsDAWindow || docHasSelection);
  1323.     EnableItemIf(menu, iCopy, frontIsDAWindow || docHasSelection);
  1324.     EnableItemIf(menu, iPaste, frontIsDAWindow || (frontIsDocWindow && clipboardHasText));
  1325.     EnableItemIf(menu, iClear, frontIsDAWindow || docHasSelection);
  1326.     EnableItemIf(menu, iSelectAll, frontIsDocWindow);
  1327.     
  1328.     menu = GetMenuHandle(mFont);
  1329.     RemoveMenuCheckMarks(menu);
  1330.     if (frontIsDocWindow)
  1331.     {
  1332.         EnableMenu(menu);
  1333.  
  1334.         mode = doFont;
  1335.         if (TEContinuousStyle(&mode, &theTextStyle, ((DocumentPeek) window)->docTE))
  1336.         {
  1337.             Str255 theName, itemName;
  1338.             short itemCount;
  1339.             
  1340.             GetFontName(theTextStyle.tsFont, theName);
  1341.             itemCount = CountMItems(menu);
  1342.             for (item = 1; item <= itemCount; item++)
  1343.             {
  1344.                 GetMenuItemText(menu, item, itemName);
  1345.                 if (EqualString(theName, itemName, true, true))
  1346.                 {
  1347.                     CheckItem(menu, item, true);
  1348.                     break;
  1349.                 };
  1350.             };
  1351.             
  1352.             haveContinuousFont = true;
  1353.             theFont = theTextStyle.tsFont;
  1354.         };
  1355.     }
  1356.     else
  1357.     {
  1358.         DisableMenu(menu);
  1359.     };
  1360.     
  1361.     menu = GetMenuHandle (mFontSize);
  1362.     RemoveMenuCheckMarks(menu);
  1363.     RemoveMenuStyles(menu);
  1364.     if (frontIsDocWindow)
  1365.     {
  1366.         EnableMenu(menu);
  1367.  
  1368.         mode = doSize;
  1369.         if (TEContinuousStyle(&mode, &theTextStyle, ((DocumentPeek) window)->docTE))
  1370.         {        
  1371.             switch (theTextStyle.tsSize)
  1372.             {
  1373.                 case 9: item = iNine; break;
  1374.                 case 10: item = iTen; break;
  1375.                 case 12: item = iTwelve; break;
  1376.                 case 14: item = iFourteen; break;
  1377.                 case 18: item = iEighteen; break;
  1378.                 case 24: item = iTwentyFour; break;
  1379.             };
  1380.             CheckItem(menu, item, true);
  1381.         };
  1382.  
  1383.         if (haveContinuousFont)
  1384.         {
  1385.             if (RealFont(theFont, 9))
  1386.                 SetItemStyle(menu, iNine, outline);
  1387.             if (RealFont(theFont, 10))
  1388.                 SetItemStyle(menu, iTen, outline);
  1389.             if (RealFont(theFont, 12))
  1390.                 SetItemStyle(menu, iTwelve, outline);
  1391.             if (RealFont(theFont, 14))
  1392.                 SetItemStyle(menu, iFourteen, outline);
  1393.             if (RealFont(theFont, 18))
  1394.                 SetItemStyle(menu, iEighteen, outline);
  1395.             if (RealFont(theFont, 24))
  1396.                 SetItemStyle(menu, iTwentyFour, outline);
  1397.         };
  1398.     }
  1399.     else
  1400.     {
  1401.         DisableMenu(menu);
  1402.     };
  1403.     
  1404.     menu = GetMenuHandle (mStyle);
  1405.     RemoveMenuCheckMarks(menu);
  1406.     if (frontIsDocWindow)
  1407.     {
  1408.         EnableMenu(menu);
  1409.  
  1410.         mode = doFace;
  1411.         if (TEContinuousStyle(&mode, &theTextStyle, ((DocumentPeek) window)->docTE))
  1412.         {
  1413.             CheckItem(menu, iPlain, theTextStyle.tsFace == normal);
  1414.             CheckItem(menu, iBold, (theTextStyle.tsFace & bold) == bold);
  1415.             CheckItem(menu, iItalic, (theTextStyle.tsFace & italic) == italic);
  1416.             CheckItem(menu, iUnderline, (theTextStyle.tsFace & underline) == underline);
  1417.             CheckItem(menu, iOutline, (theTextStyle.tsFace & outline) == outline);
  1418.             CheckItem(menu, iShadow, (theTextStyle.tsFace & shadow) == shadow);
  1419.         };        
  1420.     }
  1421.     else
  1422.     {
  1423.         DisableMenu(menu);
  1424.     };
  1425.     
  1426.     DrawMenuBar();
  1427. }
  1428.  
  1429.  
  1430. /*    This is called when an item is chosen from the menu bar (after calling
  1431.     MenuSelect or MenuKey). It does the right thing for each command. */
  1432.  
  1433. void DoMenuCommand(long menuResult)
  1434. {
  1435.     short        menuID, menuItem;
  1436.     short        itemHit, daRefNum;
  1437.     Str255        daName;
  1438.     OSErr        saveErr;
  1439.     TEHandle    te;
  1440.     WindowPtr    window;
  1441.     long        scrapLength;
  1442.     long        offset;
  1443.     Handle        aHandle;
  1444.     long        oldSize, newSize;
  1445.     long        total, contig;
  1446.     TextStyle    theTextStyle;
  1447.     Str255        theFontName;
  1448.     short        theFontID;
  1449.     short        theFontSize;
  1450.     DocumentPeek theDocument;
  1451. #if qInline
  1452.     TSMDocumentID tsmDoc;
  1453. #endif // qInline
  1454.  
  1455.     window = FrontWindow();
  1456.     menuID = HiWord(menuResult);
  1457.     menuItem = LoWord(menuResult);
  1458.  
  1459. #if qInline
  1460.     if (menuID == 0)
  1461.         // no real menu command, so we don't want to confirm inline input text
  1462.         return;
  1463.     
  1464. #endif // qInline
  1465.     if (IsDocumentWindow(window))
  1466.     {
  1467.         theDocument = (DocumentPeek) window;
  1468.         te = theDocument->docTE;
  1469. #if qInline
  1470.  
  1471.         // for any real menu command, we should first confirm inline input text if there is any
  1472.         tsmDoc = theDocument->docTSMDoc;
  1473.         if (tsmDoc != nil)
  1474.             (void) FixTSMDocument(tsmDoc);
  1475. #endif // qInline
  1476.     };
  1477.     
  1478.     switch ( menuID ) {
  1479.         case mApple:
  1480.             switch ( menuItem ) {
  1481.                 case iAbout:        /* bring up alert for About */
  1482.                     itemHit = Alert(rAboutAlert, nil);
  1483.                     break;
  1484.                 default:            /* all non-About items in this menu are DAs et al */
  1485.                     /* type Str255 is an array in MPW 3 */
  1486.                     GetMenuItemText(GetMenuHandle(mApple), menuItem, daName);
  1487.                     daRefNum = OpenDeskAcc(daName);
  1488.                     AdjustMenus();
  1489.                     break;
  1490.             }
  1491.             break;
  1492.         case mFile:
  1493.             switch ( menuItem ) {
  1494.                 case iNew:
  1495.                     DoNew();
  1496.                     break;
  1497.                 case iClose:
  1498.                     (void) DoCloseWindow(FrontWindow());            /* ignore the result */
  1499.                     break;
  1500.                 case iPageSetup:
  1501.                     PrOpen();
  1502.                     if (PrError() == noErr)
  1503.                         (void) PrStlDialog(gPrinterRecord);
  1504.                     PrClose();
  1505.                     break;
  1506.                 case iPrint:
  1507.                     PrintText(te);
  1508.                     break;
  1509.                 case iQuit:
  1510. #if qAppleEvents
  1511.                     PrepareToQuit();
  1512. #else // qAppleEvents
  1513.                     Terminate();
  1514. #endif // qAppleEvents
  1515.                     break;
  1516.             }
  1517.             break;
  1518.         case mEdit:                    /* call SystemEdit for DA editing & MultiFinder */
  1519.             if ( !SystemEdit(menuItem-1) ) {
  1520.                 switch ( menuItem ) {
  1521.                     case iCut:
  1522.                         if ( ZeroScrap() == noErr )
  1523.                         {
  1524.                             PurgeSpace(&total, &contig);
  1525.                             if ((*te)->selEnd - (*te)->selStart + kTESlop > contig)
  1526.                                 AlertUser(eNoSpaceCut);
  1527.                             else
  1528.                             {
  1529.                                 TECut(te);
  1530.                                 theDocument->modified = true;
  1531.                             };
  1532.                         };
  1533.                         break;
  1534.                     case iCopy:
  1535.                         if ( ZeroScrap() == noErr )
  1536.                             TECopy(te);
  1537.                         break;
  1538.                     case iPaste:
  1539.                         scrapLength = GetScrap(nil, 'TEXT', &offset);
  1540.                         if ( scrapLength + ((*te)->teLength -
  1541.                                 ((*te)->selEnd - (*te)->selStart)) > kMaxTELength )
  1542.                             AlertUser(eExceedPaste);
  1543.                         else
  1544.                         {
  1545.                             aHandle = (Handle) TEGetText(te);
  1546.                             oldSize = GetHandleSize(aHandle);
  1547.                             newSize = oldSize + scrapLength + kTESlop;
  1548.                             SetHandleSize(aHandle, newSize);
  1549.                             saveErr = MemError();
  1550.                             SetHandleSize(aHandle, oldSize);
  1551.                             if (saveErr != noErr)
  1552.                                 AlertUser(eNoSpacePaste);
  1553.                             else
  1554.                             {
  1555.                                 TEStylePaste(te);
  1556.                                 theDocument->modified = true;
  1557.                             };
  1558.                         };
  1559.                         break;
  1560.                     case iClear:
  1561.                         TEDelete(te);
  1562.                         theDocument->modified = true;
  1563.                         break;
  1564.                     case iSelectAll:
  1565.                         TESetSelect(0, (*te)->teLength, te);
  1566.                         break;
  1567.                 };
  1568.             if (menuItem != iCopy)
  1569.                 AdjustScrollbars(window, false);
  1570.             }
  1571.             break;
  1572.         case mFont:
  1573.             GetMenuItemText(GetMenuHandle(mFont), menuItem, theFontName);
  1574.             GetFNum(theFontName, &theFontID);
  1575.             theTextStyle.tsFont = theFontID;
  1576.             TESetStyle(doFont, &theTextStyle, true, te);
  1577.             if ((*te)->selEnd - (*te)->selStart > 0)
  1578.                 theDocument->modified = true;
  1579.             AdjustScrollbars(window, false);
  1580.             break;
  1581.         case mFontSize:
  1582.             switch (menuItem)
  1583.             {
  1584.                 case iNine:
  1585.                     theFontSize = 9;
  1586.                     break;
  1587.                 case iTen:
  1588.                     theFontSize = 10;
  1589.                     break;
  1590.                 case iTwelve:
  1591.                     theFontSize = 12;
  1592.                     break;
  1593.                 case iFourteen:
  1594.                     theFontSize = 14;
  1595.                     break;
  1596.                 case iEighteen:
  1597.                     theFontSize = 18;
  1598.                     break;
  1599.                 case iTwentyFour:
  1600.                     theFontSize = 24;
  1601.                     break;
  1602.             };
  1603.             theTextStyle.tsSize = theFontSize;
  1604.             TESetStyle(doSize, &theTextStyle, true, te);
  1605.             if ((*te)->selEnd - (*te)->selStart > 0)
  1606.                 theDocument->modified = true;
  1607.             AdjustScrollbars(window, false);
  1608.             break;
  1609.         case mStyle:
  1610.             switch (menuItem)
  1611.             {
  1612.                 case iPlain:
  1613.                     *((short *) &theTextStyle.tsFace) = 0; // see technote TE 16
  1614.                     theTextStyle.tsFace = normal;
  1615.                     break;
  1616.                 case iBold:
  1617.                     theTextStyle.tsFace = bold;
  1618.                     break;
  1619.                 case iItalic:
  1620.                     theTextStyle.tsFace = italic;
  1621.                     break;
  1622.                 case iUnderline:
  1623.                     theTextStyle.tsFace = underline;
  1624.                     break;
  1625.                 case iOutline:
  1626.                     theTextStyle.tsFace = outline;
  1627.                     break;
  1628.                 case iShadow:
  1629.                     theTextStyle.tsFace = shadow;
  1630.                     break;
  1631.             };
  1632.             if (menuItem == iPlain)
  1633.                 TESetStyle(doFace, &theTextStyle, true, te); // doToggle doesn't work for plain
  1634.             else
  1635.                 TESetStyle(doFace + doToggle, &theTextStyle, true, te);
  1636.             if ((*te)->selEnd - (*te)->selStart > 0)
  1637.                 theDocument->modified = true;
  1638.             AdjustScrollbars(window, false);
  1639.             break;
  1640.     };
  1641.     HiliteMenu(0);                    /* unhighlight what MenuSelect (or MenuKey) hilited */
  1642. }
  1643.  
  1644.  
  1645. /* Create a new document and window. */
  1646.  
  1647. void DoNew(void)
  1648. {
  1649.     Boolean        good;
  1650.     Ptr            storage;
  1651.     WindowPtr    window;
  1652.     Rect        destRect, viewRect;
  1653.     DocumentPeek doc;
  1654. #if qInline
  1655.     OSType        supportedInterfaces[1];
  1656. #endif // qInline
  1657.  
  1658.     storage = NewPtr(sizeof(DocumentRecord));
  1659.     if ( storage != nil ) {
  1660.         doc = (DocumentPeek) storage;
  1661.         doc->modified = false;
  1662. #if qInline
  1663.         doc->docTSMTERecHandle = nil;
  1664.         doc->docTSMDoc = nil;
  1665. #endif // qInline
  1666.         window = GetNewWindow(rDocWindow, storage, (WindowPtr) -1);
  1667.         if ( window != nil ) {
  1668.             gNumDocuments += 1;            /* this will be decremented when we call DoCloseWindow */
  1669.             good = false;
  1670.             SetPort(window);
  1671.             
  1672.             // on a Roman system, the default text size is 0; we need a real size to make the Size menu work
  1673.             TextSize(GetDefFontSize());
  1674.             GetTERect(window, &viewRect);
  1675.             destRect = viewRect;
  1676.             destRect.right = destRect.left + kMaxDocWidth;
  1677.             doc->docTE = TEStyleNew(&destRect, &viewRect);
  1678.             good = doc->docTE != nil;    /* if TENew succeeded, we have a good document */
  1679.             if ( good ) {                /* 1.02 - good document? — proceed */
  1680.                 TEAutoView(true, doc->docTE);
  1681.                 doc->docClick = (*doc->docTE)->clickLoop;
  1682. #if powerc
  1683.                 TESetClickLoop(gClickLoopUPP, doc->docTE);
  1684. #else
  1685.                 (*doc->docTE)->clickLoop = (TEClickLoopUPP) AsmClickLoopProc;
  1686. #endif
  1687.             }
  1688.             
  1689.             if ( good ) {                /* good document? — get scrollbars */
  1690.                 doc->docVScroll = GetNewControl(rVScroll, window);
  1691.                 good = (doc->docVScroll != nil);
  1692.             }
  1693.             if ( good) {
  1694.                 doc->docHScroll = GetNewControl(rHScroll, window);
  1695.                 good = (doc->docHScroll != nil);
  1696.             }
  1697. #if qInline
  1698.             if (good && gHasTSMTE)
  1699.             {
  1700.                 supportedInterfaces[0] = kTSMTEInterfaceType;
  1701.                 if (NewTSMDocument(1, supportedInterfaces, &doc->docTSMDoc,
  1702.                             (long) &doc->docTSMTERecHandle) == noErr)
  1703.                 {
  1704.                     TSMTERecPtr tsmteRecPtr = *(doc->docTSMTERecHandle);
  1705.                     
  1706.                     tsmteRecPtr->textH = doc->docTE;
  1707.                     tsmteRecPtr->preUpdateProc = gTSMTEPreUpdateUPP;
  1708.                     tsmteRecPtr->postUpdateProc = gTSMTEPostUpdateUPP;
  1709.                     tsmteRecPtr->updateFlag = kTSMTEAutoScroll;
  1710.                     tsmteRecPtr->refCon = (long) window;
  1711.                 }
  1712.                 else
  1713.                     good = false;
  1714.             };
  1715. #endif // qInline
  1716.  
  1717.             if ( good ) {                /* good? — adjust & draw the controls, draw the window */
  1718.                 /* false to AdjustScrollValues means musn’t redraw; technically, of course,
  1719.                 the window is hidden so it wouldn’t matter whether we called ShowControl or not. */
  1720.                 AdjustScrollValues(window, false);
  1721.                 ShowWindow(window);
  1722.                 AdjustMenus();
  1723.             } else {
  1724.                 DoCloseWindow(window);    /* otherwise regret we ever created it... */
  1725.                 AlertUser(eNoWindow);            /* and tell user */
  1726.             }
  1727.         } else
  1728.             DisposePtr(storage);            /* get rid of the storage if it is never used */
  1729.     }
  1730. } /*DoNew*/
  1731.  
  1732.  
  1733. /* Close a window. This handles desk accessory and application windows. */
  1734.  
  1735. /*    1.01 - At this point, if there was a document associated with a
  1736.     window, you could do any document saving processing if it is 'dirty'.
  1737.     DoCloseWindow would return true if the window actually closed, i.e.,
  1738.     the user didn’t cancel from a save dialog. This result is handy when
  1739.     the user quits an application, but then cancels the save of a document
  1740.     associated with a window. */
  1741.  
  1742. Boolean DoCloseWindow(WindowPtr window)
  1743. {
  1744.     DocumentPeek theDocument;
  1745.  
  1746.     if ( IsDAWindow(window) )
  1747.         CloseDeskAcc(((WindowPeek) window)->windowKind);
  1748.     else if ( IsDocumentWindow(window) ) {
  1749.         // ??? check modified flag whether document needs saving
  1750.         theDocument = (DocumentPeek) window;
  1751. #if qInline
  1752.         if (theDocument->docTSMDoc != nil)
  1753.         {
  1754.             (void) FixTSMDocument(theDocument->docTSMDoc);
  1755.             // DeleteTSMDocument might cause crash if we don't deactivate first, so...
  1756.             (void) DeactivateTSMDocument(theDocument->docTSMDoc);
  1757.             (void) DeleteTSMDocument(theDocument->docTSMDoc);
  1758.         };
  1759. #endif // qInline
  1760.         if (theDocument->docTE != nil)
  1761.             TEDispose(theDocument->docTE);
  1762.         /*    1.01 - We used to call DisposeWindow, but that was technically
  1763.             incorrect, even though we allocated storage for the window on
  1764.             the heap. We should instead call CloseWindow to have the structures
  1765.             taken care of and then dispose of the storage ourselves. */
  1766.         CloseWindow(window);
  1767.         DisposePtr((Ptr) window);
  1768.         gNumDocuments -= 1;
  1769.     }
  1770.     AdjustMenus();
  1771.     return true;
  1772. } /*DoCloseWindow*/
  1773.  
  1774.  
  1775. // PrintText prints the text in the edit record. It opens a printer port, calculates
  1776. // the number of lines per page (which may be different for each page depending on the
  1777. // text styles) and then calls TEUpdate for the page, scrolls a page and calls TEUpdate,
  1778. // etc.
  1779.  
  1780. static void PrintText(TEHandle theText)
  1781. {
  1782.     const short kMargin = 20; // page margins in pixels
  1783.     const Rect zeroRect = { 0, 0, 0, 0 };
  1784.     short totalLines;
  1785.     GrafPtr oldPort;
  1786.     Rect oldViewRect;
  1787.     Rect oldDestRect;
  1788.     Rect viewRect;
  1789.     Rect updateRect;
  1790.     Rect clipRect;
  1791.     short totalHeight;
  1792.     short currentLine;
  1793.     short scrollAmount;
  1794.     TPrStatus thePrinterStatus;
  1795.     Boolean printManagerIsOpen = false;
  1796.     Boolean userHasCancelled = false;
  1797.     short viewHeight;
  1798.     TPPrPort thePrinterPort;
  1799.     
  1800.     if (gPrinterRecord != nil)
  1801.     {
  1802.         PrOpen();
  1803.         if (PrJobDialog(gPrinterRecord))
  1804.         {
  1805.             GetPort(&oldPort);
  1806.             oldViewRect = (*theText)->viewRect;
  1807.             oldDestRect = (*theText)->destRect;
  1808.             thePrinterPort = PrOpenDoc(gPrinterRecord, nil, nil);
  1809.             printManagerIsOpen = (PrError() == noErr);
  1810.         };
  1811.     };
  1812.     
  1813.     if (printManagerIsOpen)
  1814.     {
  1815.         SetPort((GrafPtr) thePrinterPort);
  1816.         
  1817.         // re-wrap the text to fill the entire page minus margins
  1818.         viewRect = (*gPrinterRecord)->prInfo.rPage;
  1819.         InsetRect(&viewRect, kMargin, kMargin);
  1820.         (*theText)->inPort = (GrafPtr) thePrinterPort;
  1821.         (*theText)->destRect = viewRect;
  1822.         (*theText)->viewRect = viewRect;
  1823.         TECalText(theText);
  1824.         totalLines = (*theText)->nLines;
  1825.         totalHeight = TEGetHeight(totalLines, 0, theText);
  1826.         (*theText)->destRect.bottom = (*theText)->destRect.top + totalHeight;
  1827.         
  1828.         currentLine = 1;
  1829.         
  1830.         while ((!userHasCancelled) && (currentLine <= totalLines))
  1831.         {
  1832.             PrOpenPage(thePrinterPort, nil);
  1833.             scrollAmount = 0;
  1834.             clipRect = (*gPrinterRecord)->prInfo.rPage;
  1835.             ClipRect(&clipRect);
  1836.             
  1837.             viewHeight = (*theText)->viewRect.bottom - (*theText)->viewRect.top + 1;
  1838.             
  1839.             while (((scrollAmount + TEGetHeight(currentLine, currentLine, theText)) <= viewHeight)
  1840.                         && (currentLine <= totalLines))
  1841.             {
  1842.                 scrollAmount += TEGetHeight(currentLine, currentLine, theText);
  1843.                 currentLine++;
  1844.             };
  1845.             
  1846.             (*theText)->viewRect.bottom = scrollAmount + kMargin;
  1847.             TEDeactivate(theText); // avoid printing selections
  1848.             updateRect = (*theText)->viewRect;
  1849.             TEUpdate(&updateRect, theText);
  1850.             ClipRect(&zeroRect); // prevent TEScroll from redrawing the text
  1851.             TEScroll(0, -scrollAmount, theText); // scroll so we can print the next page
  1852.             (*theText)->viewRect.bottom = viewRect.bottom; // reset to full page;
  1853.             
  1854.             if (PrError() == iPrAbort)
  1855.                 userHasCancelled = true;
  1856.             PrClosePage(thePrinterPort);
  1857.         };
  1858.         
  1859.         PrCloseDoc(thePrinterPort);
  1860.  
  1861.         if ((*gPrinterRecord)->prJob.bJDocLoop == bSpoolLoop && PrError() == noErr)
  1862.             PrPicFile(gPrinterRecord, nil, nil, nil, &thePrinterStatus);
  1863.         PrClose();
  1864.         
  1865.         SetPort(oldPort);
  1866.         (*theText)->inPort = oldPort;
  1867.         (*theText)->viewRect = oldViewRect;
  1868.         (*theText)->destRect = oldDestRect;
  1869.         TECalText(theText);
  1870.         updateRect = (*theText)->viewRect;
  1871.         TEUpdate(&updateRect, theText);
  1872.     };
  1873. }
  1874.  
  1875. #if qAppleEvents
  1876.  
  1877. // handle the Quit menu command or Apple event by closing all windows, and
  1878. // setting gQuitting if successful.
  1879.  
  1880. static void PrepareToQuit(void)
  1881. {
  1882.     WindowPtr aWindow;
  1883.     
  1884.     gQuitting = true;
  1885.     aWindow = FrontWindow();
  1886.  
  1887.     while (gQuitting && (aWindow != nil))
  1888.     {
  1889.         gQuitting = DoCloseWindow(aWindow);
  1890.         aWindow = FrontWindow();
  1891.     };
  1892. }
  1893.  
  1894. #if qInline
  1895.  
  1896. static void ExitApplication(void)
  1897. {
  1898.     if (gHasTextServices)
  1899.         (void) CloseTSMAwareApplication();
  1900.  
  1901.     // set global fontForce flag so other apps don't get confused
  1902.     (void) SetScriptManagerVariable(smFontForce, gSavedFontForce);
  1903.  
  1904.     ExitToShell();
  1905. }
  1906.  
  1907. #endif // qInline
  1908. #else // qAppleEvents
  1909.  
  1910. // handle the Quit menu command by closing all windows, and quitting if successful.
  1911.  
  1912. static void Terminate(void)
  1913. {
  1914.     WindowPtr aWindow;
  1915.     Boolean closed;
  1916.     
  1917.     closed = true;
  1918.     aWindow = FrontWindow();
  1919.  
  1920.     while (closed && (aWindow != nil))
  1921.     {
  1922.         closed = DoCloseWindow(aWindow);
  1923.         aWindow = FrontWindow();
  1924.     };
  1925.  
  1926.     if (closed)
  1927.         ExitToShell(); // exit if no cancellation
  1928. }
  1929.  
  1930. #endif // qAppleEvents
  1931.  
  1932. /* Return a rectangle that is inset from the portRect by the size of
  1933.     the scrollbars and a little extra margin. */
  1934.  
  1935. void GetTERect(WindowPtr window, Rect *teRect)
  1936. {
  1937.     *teRect = window->portRect;
  1938.     InsetRect(teRect, kTextMargin, kTextMargin);    /* adjust for margin */
  1939.     teRect->bottom = teRect->bottom - 15;        /* and for the scrollbars */
  1940.     teRect->right = teRect->right - 15;
  1941. } /*GetTERect*/
  1942.  
  1943.  
  1944. /* Update the TERec's view rect so that it is the greatest multiple of
  1945.     the lineHeight that still fits in the old viewRect. */
  1946.  
  1947. void AdjustViewRect(TEHandle docTE)
  1948. {
  1949.     TEPtr        te;
  1950.     
  1951.     te = *docTE;
  1952.     te->viewRect.bottom = (((te->viewRect.bottom - te->viewRect.top) / te->lineHeight)
  1953.                             * te->lineHeight) + te->viewRect.top;
  1954. } /*AdjustViewRect*/
  1955.  
  1956.  
  1957. /* Scroll the TERec around to match up to the potentially updated scrollbar
  1958.     values. This is really useful when the window has been resized such that the
  1959.     scrollbars became inactive but the TERec was already scrolled. */
  1960.  
  1961. void AdjustTE(WindowPtr window)
  1962. {
  1963.     TEPtr        te;
  1964.     
  1965.     te = *((DocumentPeek)window)->docTE;
  1966.     TEScroll((te->viewRect.left - te->destRect.left) -
  1967.             GetControlValue(((DocumentPeek)window)->docHScroll),
  1968.             (te->viewRect.top - te->destRect.top) -
  1969.                 GetControlValue(((DocumentPeek)window)->docVScroll),
  1970.             ((DocumentPeek)window)->docTE);
  1971. } /*AdjustTE*/
  1972.  
  1973.  
  1974. /* Calculate the new control maximum value and current value, whether it is the horizontal or
  1975.     vertical scrollbar. The vertical max is calculated by comparing the number of lines to the
  1976.     vertical size of the viewRect. The horizontal max is calculated by comparing the maximum document
  1977.     width to the width of the viewRect. The current values are set by comparing the offset between
  1978.     the view and destination rects. If necessary and we canRedraw, have the control be re-drawn by
  1979.     calling ShowControl. */
  1980.  
  1981. // for styled text, we cannot rely on the line height being constant, so we
  1982. // have to use pixels instead of nLines
  1983.  
  1984. void AdjustHV(Boolean isVert, ControlHandle control, TEHandle docTE, Boolean canRedraw)
  1985. {
  1986.     short        value, max;
  1987.     short        oldValue, oldMax;
  1988.     
  1989.     oldValue = GetControlValue(control);
  1990.     oldMax = GetControlMaximum(control);
  1991.     if (isVert)
  1992.         max = TEGetHeight((*docTE)->nLines, 0, docTE) -
  1993.                     ((*docTE)->viewRect.bottom - (*docTE)->viewRect.top);
  1994.     else
  1995.         max = kMaxDocWidth - ((*docTE)->viewRect.right - (*docTE)->viewRect.left);
  1996.     if (max < 0)
  1997.         max = 0;
  1998.     SetControlMaximum(control, max);
  1999.     if (isVert)
  2000.         value = (*docTE)->viewRect.top - (*docTE)->destRect.top;
  2001.     else
  2002.         value = (*docTE)->viewRect.left - (*docTE)->destRect.left;
  2003.     
  2004.     if ( value < 0 )
  2005.         value = 0;
  2006.     else if ( value >  max ) value = max;
  2007.     
  2008.     SetControlValue(control, value);
  2009.     /* now redraw the control if it needs to be and can be */
  2010.     if ( canRedraw || (max != oldMax) || (value != oldValue) )
  2011.         ShowControl(control);
  2012. } /*AdjustHV*/
  2013.  
  2014.  
  2015. /* Simply call the common adjust routine for the vertical and horizontal scrollbars. */
  2016.  
  2017. void AdjustScrollValues(WindowPtr window, Boolean canRedraw)
  2018. {
  2019.     DocumentPeek doc;
  2020.     
  2021.     doc = (DocumentPeek)window;
  2022.     AdjustHV(true, doc->docVScroll, doc->docTE, canRedraw);
  2023.     AdjustHV(false, doc->docHScroll, doc->docTE, canRedraw);
  2024. } /*AdjustScrollValues*/
  2025.  
  2026.  
  2027. /*    Re-calculate the position and size of the viewRect and the scrollbars.
  2028.     kScrollTweek compensates for off-by-one requirements of the scrollbars
  2029.     to have borders coincide with the growbox. */
  2030.  
  2031. void AdjustScrollSizes(WindowPtr window)
  2032. {
  2033.     Rect        teRect;
  2034.     DocumentPeek doc;
  2035.     
  2036.     doc = (DocumentPeek) window;
  2037.     GetTERect(window, &teRect);                            /* start with TERect */
  2038.     (*doc->docTE)->viewRect = teRect;
  2039.     MoveControl(doc->docVScroll, window->portRect.right - kScrollbarAdjust, -1);
  2040.     SizeControl(doc->docVScroll, kScrollbarWidth, (window->portRect.bottom - 
  2041.                 window->portRect.top) - (kScrollbarAdjust - kScrollTweek));
  2042.     MoveControl(doc->docHScroll, -1, window->portRect.bottom - kScrollbarAdjust);
  2043.     SizeControl(doc->docHScroll, (window->portRect.right - 
  2044.                 window->portRect.left) - (kScrollbarAdjust - kScrollTweek),
  2045.                 kScrollbarWidth);
  2046. } /*AdjustScrollSizes*/
  2047.  
  2048.  
  2049. /* Turn off the controls by jamming a zero into their contrlVis fields (HideControl erases them
  2050.     and we don't want that). If the controls are to be resized as well, call the procedure to do that,
  2051.     then call the procedure to adjust the maximum and current values. Finally re-enable the controls
  2052.     by jamming a $FF in their contrlVis fields. */
  2053.  
  2054. void AdjustScrollbars(WindowPtr window, Boolean needsResize)
  2055. {
  2056.     DocumentPeek doc;
  2057.     
  2058.     doc = (DocumentPeek) window;
  2059.     /* First, turn visibility of scrollbars off so we won’t get unwanted redrawing */
  2060.     (*doc->docVScroll)->contrlVis = kControlInvisible;    /* turn them off */
  2061.     (*doc->docHScroll)->contrlVis = kControlInvisible;
  2062.     if ( needsResize )                                    /* move & size as needed */
  2063.         AdjustScrollSizes(window);
  2064.     AdjustScrollValues(window, needsResize);            /* fool with max and current value */
  2065.     /* Now, restore visibility in case we never had to ShowControl during adjustment */
  2066.     (*doc->docVScroll)->contrlVis = kControlVisible;    /* turn them on */
  2067.     (*doc->docHScroll)->contrlVis = kControlVisible;
  2068. } /* AdjustScrollbars */
  2069.  
  2070.  
  2071. // When the user selects text by dragging, TextEdit repeatedly calls a click loop routine which
  2072. // it gets from the TERecord's clikLoop field. TextEdit's default routine does some useful things,
  2073. // such as scrolling the text being selected, but it doesn't know about our scroll bars.
  2074. // Therefore, we replace the routine with one that calls both the old routine and an add-on routine
  2075. // which handles the scroll bars. Unfortunately, the way this works is very different for 68K and
  2076. // PowerPC. On 68K, we have to be aware that the original click loop routine has a register-based
  2077. // interface, so our replacement is easier to write in assembly. For PowerPC, we can let routine
  2078. // descriptors handle the argument conversions, and do everything in the C routine ClickLoopProc.
  2079.  
  2080. #if powerc
  2081. pascal Boolean ClickLoopProc(TEPtr pTE)
  2082. {
  2083.     CallTEClickLoopProc(GetOldClickLoop(), pTE);
  2084.     ClickLoopAddOn();
  2085.     return true;
  2086. }
  2087. #endif
  2088.  
  2089.  
  2090. // The ClickLoopAddOn routine handles the scroll bars during drag-scrolling.
  2091.  
  2092. pascal void ClickLoopAddOn(void)
  2093. {
  2094.     WindowPtr    window;
  2095.     RgnHandle    region;
  2096.     
  2097.     window = FrontWindow();
  2098.     region = NewRgn();
  2099.     GetClip(region);                    /* save clip */
  2100.     ClipRect(&window->portRect);
  2101.     AdjustScrollValues(window, true);    /* pass true for canRedraw */
  2102.     SetClip(region);                    /* restore clip */
  2103.     DisposeRgn(region);
  2104. }
  2105.  
  2106.  
  2107. // GetOldClickLoop returns the address of the default click loop routine that we put into the
  2108. // TERec when creating it.
  2109.  
  2110. pascal TEClickLoopUPP GetOldClickLoop(void)
  2111. {
  2112.     return ((DocumentPeek)FrontWindow())->docClick;
  2113. }
  2114.  
  2115.  
  2116. // Check whether a window is a document window created by the application.
  2117. // These windows have the windowKind userKind, so we can distinguish them from
  2118. // desk accessories, dialogs, and other windows.
  2119.  
  2120. Boolean IsDocumentWindow(WindowPtr window)
  2121. {
  2122.     return (window != nil) && (((WindowPeek) window)->windowKind == userKind);
  2123. }
  2124.  
  2125.  
  2126. // Check whether a window belongs to a desk accessory.
  2127. // These windows have negative windowKinds.
  2128.  
  2129. Boolean IsDAWindow(WindowPtr window)
  2130. {
  2131.     return (window != nil) && (((WindowPeek) window)->windowKind < 0);
  2132. }
  2133.  
  2134.  
  2135. /*    Display an alert that tells the user an error occurred, then exit the program.
  2136.     This routine is used as an ultimate bail-out for serious errors that prohibit
  2137.     the continuation of the application. Errors that do not require the termination
  2138.     of the application should be handled in a different manner. Error checking and
  2139.     reporting has a place even in the simplest application. The error number is used
  2140.     to index an 'STR#' resource so that a relevant message can be displayed. */
  2141.  
  2142. void AlertUser(short error)
  2143. {
  2144.     short        itemHit;
  2145.     Str255        message;
  2146.  
  2147.     SetCursor(&qd.arrow);
  2148.     /* type Str255 is an array in MPW 3 */
  2149.     GetIndString(message, rErrorStrings, error);
  2150.     ParamText(message, (ConstStr255Param) "", (ConstStr255Param) "", (ConstStr255Param) "");
  2151.     itemHit = Alert(rUserAlert, nil);
  2152. } /* AlertUser */
  2153.  
  2154. #if qAppleEvents
  2155.  
  2156. // Apple Event Support
  2157.  
  2158. static OSErr GotRequiredParameters(const AppleEvent *theAppleEvent)
  2159. {
  2160.     OSErr myErr;
  2161.     DescType returnedType;
  2162.     Size actualSize;
  2163.     
  2164.     myErr = AEGetAttributePtr(theAppleEvent, keyMissedKeywordAttr, typeWildCard, &returnedType,
  2165.                 nil, 0, &actualSize);
  2166.     if (myErr == errAEDescNotFound)
  2167.         return noErr;
  2168.     else if (myErr == noErr)
  2169.         return errAEParamMissed;
  2170.     else
  2171.         return myErr;
  2172. }
  2173.  
  2174. pascal OSErr HandleOAppEvent(const AppleEvent *theEvent, const AppleEvent *reply, long refCon)
  2175. {
  2176.     #pragma unused(reply, refCon)
  2177.     
  2178.     OSErr theError;
  2179.     
  2180.     theError = GotRequiredParameters(theEvent);
  2181.     if (theError == noErr)
  2182.         DoNew();
  2183.     return theError;
  2184. }
  2185.  
  2186. pascal OSErr HandleDocEvent(const AppleEvent *theEvent, const AppleEvent *reply, long refCon)
  2187. {
  2188.     #pragma unused(theEvent, reply, refCon)
  2189.     
  2190.     OSErr theError;
  2191.     AEDescList docList;
  2192.     long itemsInList;
  2193.     long index;
  2194.     AEKeyword keyword;
  2195.     DescType returnedType;
  2196.     FSSpec theFileSpec;
  2197.     Size actualSize;
  2198.     
  2199.     
  2200.     theError = AEGetParamDesc(theEvent, keyDirectObject, typeAEList,  &docList);
  2201.     if (theError == noErr)
  2202.     {
  2203.         theError = GotRequiredParameters(theEvent);
  2204.         if (theError == noErr)
  2205.         {
  2206.             theError = AECountItems(&docList, &itemsInList);
  2207.             if (theError == noErr)
  2208.             {
  2209.                 for (index = 1; index <= itemsInList; index++)
  2210.                 {
  2211.                     theError = AEGetNthPtr(&docList, index, typeFSS, &keyword, &returnedType,
  2212.                                 (Ptr) &theFileSpec, sizeof(theFileSpec), &actualSize);
  2213.                     if (theError == noErr)
  2214.                     {
  2215.                         if (refCon == kAEOpenDocuments)
  2216.                             // we don't open documents yet, but here's what it would look like:
  2217.                             // theError = OpenDocument(theFileSpec);
  2218.                             ;
  2219.                         else
  2220.                             // we don't print disk documents either (we can't read them),
  2221.                             // but here's what it would look like:
  2222.                             // theError = PrintDocument(theFileSpec);
  2223.                             ;
  2224.                     };
  2225.                 };
  2226.             };
  2227.         };
  2228.         (void) AEDisposeDesc(&docList);
  2229.     };
  2230.     return theError;
  2231. }
  2232.  
  2233. pascal OSErr HandleQuitEvent(const AppleEvent *theEvent, const AppleEvent *reply, long refCon)
  2234. {
  2235.     #pragma unused(reply, refCon)
  2236.     
  2237.     OSErr theError;
  2238.     
  2239.     theError = GotRequiredParameters(theEvent);
  2240.     if (theError == noErr)
  2241.     {
  2242.         PrepareToQuit();
  2243.         if (!gQuitting)
  2244.             theError = userCanceledErr;
  2245.     };
  2246.     return theError;
  2247. }
  2248.  
  2249. static OSErr InstallRequiredAppleEvents(void)
  2250. {
  2251.     OSErr result;
  2252.     
  2253.     gHandleOAppUPP = NewAEEventHandlerProc(HandleOAppEvent);
  2254.     FailNilUPP((UniversalProcPtr) gHandleOAppUPP);
  2255.     gHandleDocUPP = NewAEEventHandlerProc(HandleDocEvent);
  2256.     FailNilUPP((UniversalProcPtr) gHandleDocUPP);
  2257.     gHandleQuitUPP = NewAEEventHandlerProc(HandleQuitEvent);
  2258.     FailNilUPP((UniversalProcPtr) gHandleQuitUPP);
  2259.  
  2260.     result = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication,
  2261.                 gHandleOAppUPP, 0, false);
  2262.     if (result == noErr)
  2263.         result = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,
  2264.                     gHandleDocUPP, kAEOpenDocuments, false);
  2265.     if (result == noErr)
  2266.         result = AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments,
  2267.                     gHandleDocUPP, kAEPrintDocuments, false);
  2268.     if (result == noErr)
  2269.         result = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
  2270.                     gHandleQuitUPP, 0, false);
  2271.     return result;
  2272. }
  2273.  
  2274. #if qInline
  2275.  
  2276. static pascal void MyTSMTEPreUpdateProc(TEHandle textH, long refCon)
  2277. {
  2278.     #pragma unused(refCon)
  2279.  
  2280.     long response;
  2281.     ScriptCode keyboardScript;
  2282.     short mode;
  2283.     TextStyle theStyle;
  2284.     
  2285.     if ((Gestalt(gestaltTSMTEVersion, &response) == noErr) && (response == gestaltTSMTE1))
  2286.     {
  2287.         keyboardScript = GetScriptManagerVariable(smKeyScript);
  2288.         mode = doFont;
  2289.         if (!(TEContinuousStyle(&mode, &theStyle, textH) &&
  2290.                 FontToScript(theStyle.tsFont) == keyboardScript))
  2291.         {
  2292.             theStyle.tsFont = GetScriptVariable(keyboardScript, smScriptAppFond);
  2293.             TESetStyle(doFont, &theStyle, false, textH);
  2294.         };
  2295.     };
  2296. }
  2297.  
  2298. static pascal void MyTSMTEPostUpdateProc(TEHandle textH, long fixLen, long inputAreaStart,
  2299.             long inputAreaEnd, long pinStart, long pinEnd, long refCon)
  2300. {
  2301.     #pragma unused(textH, fixLen, inputAreaStart, inputAreaEnd, pinStart, pinEnd)
  2302.     
  2303.     AdjustScrollbars((WindowPtr) refCon, false);
  2304.     AdjustTE((WindowPtr) refCon);
  2305. }
  2306.  
  2307. #endif // qInline
  2308. #endif // qAppleEvents